package com.zingrow.web.report.service.impl;

import java.awt.Dimension;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGrid;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblGridCol;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zingrow.web.report.base.TableField;
import com.zingrow.web.report.response.ModuleData;
import com.zingrow.web.report.service.IDocExportor;
import com.zingrow.web.report.utils.DateUtils;
import com.zingrow.web.report.utils.ReportUtils;
import com.zingrow.web.report.utils.RptJsonUtils;

/**
 * Word数据处理<br>
 * 使用Apache_POI_3.8-XWPF生成简易Word文档
 * 
 * @author sywd_吴迪
 *
 */
public class WordDocExportor implements IDocExportor {
    private OutputStream out;

    /**
     * 根据数据解析后写入到word文档
     * 
     * @param infos
     *            一些标题注释信息等
     * @param moduleDatas
     *            排序的模块数据，包括标题等信息
     * @param svg
     *            有图表的svg字符串，根据模块信息里的标题取出，并生成图片插入word
     * @param out
     *            word的输出流对象
     * @return
     */
    @Override
    public IDocExportor createDoc(Map<String, String> infos, List<ModuleData> moduleDatas, Map<String, String> svg,
            OutputStream out) {
        this.out = out;
        try {
            writeLine();
            // 写入报告的标题信息
            writeText(infos.get(GAROUP_NAME), DocFont.TitleDoc, Align.Center);
            writeText(infos.get(RPT_NAME), DocFont.TitleDoc, Align.Center);
            writeLine();
            // 遍历获取到的模块数据信息，依次写入Word
            for (ModuleData moduleData : moduleDatas) {
                // 判断模块是否显示
                if (!moduleData.getShow()) {
                    continue;
                }
                // 写入模块标题
                String moduleTitle = moduleData.getTitle();
                writeText(moduleTitle, DocFont.TitleModule, Align.Left);
                // 写入模块的文字信息
                String moduleText = moduleData.getText();
                if (StringUtils.isNoneBlank(moduleText)) {
                    writeText(moduleText, DocFont.Content, Align.Left);
                }
                // 开始插入图片
                String svgStr = svg.get(moduleData.getId() + "");
                // 根据展示类型判断是否加载并写入图片>2表示表格以上
                if (moduleData.getTypeId() > 2 && StringUtils.isNoneBlank(svgStr)) {
                    byte[] bytes = ReportUtils.convertToPngByte(svgStr);
                    writeImage(bytes, null, null);
                } else if (moduleData.getTypeId() == 2 && !moduleData.getDataList().isEmpty()
                        && !moduleData.getTableFields().isEmpty()) {// 绘制表格
                    // 将数据信息转换为json数据
                    String dataJson = RptJsonUtils.toString(moduleData.getDataList(), "yyyy-MM-dd HH:mm:ss");
                    // 计算行数
                    int rowSize = 0;
                    if (moduleData.isPullByList()) {
                        rowSize = moduleData.getDataList().size() * moduleData.getDataList().get(0).getDatas().size();
                    } else {
                        rowSize = moduleData.getDataList().get(0).getDatas().size();
                    }
                    writeTable(moduleData.getTableFields(), dataJson, moduleData.isPullByList(), rowSize);
                } else if (moduleData.getTypeId() == 1 && moduleData.getDataList() != null
                        && moduleData.getDataList().size() > 0) {// 填充文字列表
                    String dataJson = RptJsonUtils.toString(moduleData.getDataList(), "yyyy-MM-dd HH:mm:ss");
                    writeTextList(moduleData.getTableFields(), dataJson);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this;
    }

    @Override
    public void writeToStream() throws Exception {
        getDoc().write(out);
    }

    @Override
    public void writeImage(byte[] bytes, Dimension size, Align align) throws Exception {
        if (bytes.length == 0) {
            return;
        }
        if (size == null) {
            size = ReportUtils.getImgDimension(new ByteArrayInputStream(bytes));
        }
        if (size == null) {
            return;
        }
        if (align == null) {
            align = Align.Center;
        }
        XWPFParagraph xwpf = createXWPFParagraph(align);
        xwpf.createRun().addPicture(new ByteArrayInputStream(bytes), XWPFDocument.PICTURE_TYPE_PNG, "",
                Units.toEMU(size.getWidth()), Units.toEMU(size.getHeight()));
    }

    @Override
    public void writeText(String text, DocFont font, Align align) throws Exception {
        if (StringUtils.isBlank(text)) {
            return;
        }
        font = font == null ? DocFont.TitleModule : font;
        align = align == null ? Align.Left : align;
        XWPFParagraph xwpf = createXWPFParagraph(align);
        XWPFRun xwpfRun = xwpf.createRun();
        xwpfRun.setText(text);
        xwpfRun.setBold(font.getBold());
        xwpfRun.setFontFamily(font.getFont());
        xwpfRun.setFontSize(font.getSize());
    }

    /**
     * @Description: 得到单元格第一个Paragraph
     */
    private XWPFParagraph getCellFirstParagraph(XWPFTableCell cell) {
        XWPFParagraph p;
        if (cell.getParagraphs() != null && cell.getParagraphs().size() > 0) {
            p = cell.getParagraphs().get(0);
        } else {
            p = cell.addParagraph();
        }
        return p;
    }

    private void setCellContent(XWPFTable table, int row, int col, DocFont font, String text) {
        XWPFTableCell cell = table.getRow(row).getCell(col);
        XWPFParagraph paragraph = getCellFirstParagraph(cell);
        paragraph.setAlignment(getAlign(Align.Center));
        List<XWPFRun> cellRunList = paragraph.getRuns();
        XWPFRun run = null;
        if (cellRunList == null || cellRunList.size() == 0) {
            run = paragraph.createRun();
        } else {
            for (int o = cellRunList.size() - 1; o >= 1; o--) {
                paragraph.removeRun(o);
            }
            // 设置标题信息
            run = cellRunList.get(0);
        }
        run.setBold(font.getBold());
        run.setFontSize(font.getSize());
        run.setFontFamily(font.getFont());
        run.setText(text);
    }

    @Override
    public void writeTable(List<TableField> fields, String dataJson, boolean isPullByList, int rowSize)
            throws Exception {
        // 获取列数量
        int colSize = fields.size();
        // 根据计算的行列来新建一个保存表格元素的二维数组（这里的行数包含标题项）
        rowSize += 1;
        // 从json数据中解析出树
        JsonNode root = new ObjectMapper().readTree(dataJson);
        // 表格的总宽度
        int totalWidth = 9000;
        // 这里计算每一列的宽度占比
        int[] widths = new int[colSize];
        for (int i = 0; i < colSize; i++) {
            widths[i] = (int) (totalWidth * fields.get(i).getWidthPercent());
        }
        // 开始生成表格
        XWPFTable table = getDoc().createTable(rowSize, colSize);
        // 设置表格的样式、宽度等，初始化操作
        CTTbl ttbl = table.getCTTbl();
        CTTblPr tablePr = ttbl.getTblPr() == null ? ttbl.addNewTblPr() : ttbl.getTblPr();
        CTTblWidth width = tablePr.isSetTblW() ? tablePr.getTblW() : tablePr.addNewTblW();
        width.setW(BigInteger.valueOf(totalWidth));
        CTTblGrid tblGrid = ttbl.getTblGrid() != null ? ttbl.getTblGrid() : ttbl.addNewTblGrid();
        for (int j = 0, len = widths.length; j < len; j++) {
            CTTblGridCol gridCol = tblGrid.addNewGridCol();
            gridCol.setW(new BigInteger(String.valueOf(widths[j])));
        }
        // BaseColor lightGrey = new BaseColor(0xCC, 0xCC, 0xCC);
        // BaseColor firstColor = new BaseColor(77, 83, 97);
        // 下面的for生成标题栏
        for (int i = 0; i < colSize; i++) {
            setCellContent(table, 0, i, DocFont.TitleModule, fields.get(i).getTitle());
        }
        // 行数计数器.为了精准添加数据到表格的坐标而计算的行数值
        int row = 0;
        // 开始解析并填充表格数据
        Iterator<JsonNode> moduleDataIterator = root.elements();
        while (moduleDataIterator.hasNext()) {
            // 如果不是按照普通的表格排列，则表格的行数不是数据类型列表数*值列表数，而只是数据列表数
            // 故重新设置行数为0
            if (!isPullByList) {
                row = 0;
            }
            // 拿出第一层数据列表
            JsonNode typeData = moduleDataIterator.next();
            // 获取这个数据的标题
            String dataName = typeData.findValue("title").asText();
            // 获取到这一行的数据
            JsonNode dataList = typeData.path("datas");
            // 在类型数据中获取真正值数据的迭代。
            Iterator<JsonNode> dataIterator = dataList.elements();
            while (dataIterator.hasNext()) {
                // 每循环一个数据对象，行数+1,这里包括了标题这一行，所以要提前+1
                row++;
                // 这个data代表一个数据信息，即最终取到的包含值的对象
                JsonNode data = dataIterator.next();
                // 再次遍历列标题信息数组，进行field匹配，匹配到的则取出值

                for (int i = 0; i < colSize; i++) {
                    // 获取这一列定义的的标题信息
                    TableField field = fields.get(i);
                    // 拼接显示的文本，定义一个文本对象
                    StringBuilder value = new StringBuilder();
                    String dataListField = field.getDataListField();
                    String valueField = field.getValueField();
                    // 格式化文本的format
                    String textFormat = field.getTextFormat();
                    // 格式化日期的format
                    String dateFormat = field.getTimeFormat();
                    // 超链接的field
                    String urlField = field.getUrlField();

                    // 若匹配到此列跟此行数据来源一致
                    if (StringUtils.isNotBlank(dataName) && StringUtils.isNotBlank(dataListField)
                            && dataListField.equals(dataName)) {
                        String str = "";
                        // 首先判断是否需要自增的序号
                        if (field.isIncr()) {
                            value.append(row+ "");
                        }
                        // 取出文本值
                        if (StringUtils.isNotBlank(valueField)) {
                            try {
                                str = data.findValue(valueField).asText();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            if (StringUtils.isNotBlank(textFormat)) {
                                // 判断是否需要格式化填充文本
                                str = String.format(textFormat, str);
                            } else if (StringUtils.isNotBlank(dateFormat)) {
                                // 判断是否需要格式化文本日期
                                try {
                                    str = DateUtils.printDate(DateUtils.paraseStringToDate(str, "yyyy-MM-dd HH:mm:ss"),
                                            field.getTimeFormat());
                                } catch (Exception e) {
                                }
                            }
                            // 最后拼接文本则为最后显示的
                            value.append(str);
                        }
                        // 这里字体需要新建对象，否则影响其他的字体

                        // 创建字体和表格元素对象
                        setCellContent(table, row, i, DocFont.Content, value.toString());
                    } else {
                        // 没有找到匹配的field字段
                        continue;
                    }
                }
            }
        }
    }

    @Override
    public void writeTextList(List<TableField> fields, String dataJson) throws Exception {
        // 获取列数量
        int colSize = fields.size();
        // 从json数据中解析出树
        JsonNode root = new ObjectMapper().readTree(dataJson);
        int row = 0;
        // 开始解析并填充表格数据
        Iterator<JsonNode> moduleDataIterator = root.elements();
        while (moduleDataIterator.hasNext()) {
            // 拿出第一层数据列表
            JsonNode typeData = moduleDataIterator.next();
            // 获取这个数据的标题
            String dataName = typeData.findValue("title").asText();
            // 获取到这一行的数据
            JsonNode dataList = typeData.path("datas");
            // 在类型数据中获取真正值数据的迭代。
            Iterator<JsonNode> dataIterator = dataList.elements();
            while (dataIterator.hasNext()) {
                // 这个data代表一个数据信息，即最终取到的包含值的对象
                JsonNode data = dataIterator.next();
                // 再次遍历列标题信息数组，进行field匹配，匹配到的则取出值
                for (int i = 0; i < colSize; i++) {
                    // 获取这一列定义的的标题信息
                    TableField field = fields.get(i);
                    // 拼接显示的文本，定义一个文本对象
                    StringBuilder value = new StringBuilder();
                    String dataListField = field.getDataListField();
                    String valueField = field.getValueField();
                    // 格式化文本的format
                    String textFormat = field.getTextFormat();
                    // 格式化日期的format
                    String dateFormat = field.getTimeFormat();

                    // 若匹配到此列跟此行数据来源一致
                    if (StringUtils.isNotBlank(dataName) && StringUtils.isNotBlank(dataListField)
                            && dataListField.equals(dataName)) {
                        String str = "";
                        // 首先判断是否需要自增的序号
                        if (field.isIncr()) {
                            value.append((row + 1) + "");
                        }
                        // 取出文本值
                        if (StringUtils.isNotBlank(valueField)) {
                            str = data.findValue(field.getValueField()).asText();
                            if (StringUtils.isNotBlank(textFormat)) {
                                // 判断是否需要格式化填充文本
                                str = String.format(textFormat, str);
                            } else if (StringUtils.isNotBlank(dateFormat)) {
                                // 判断是否需要格式化文本日期
                                try {
                                    str = DateUtils.printDate(DateUtils.paraseStringToDate(str, "yyyy-MM-dd HH:mm:ss"),
                                            field.getTimeFormat());
                                } catch (Exception e) {
                                }
                            }
                            // 最后拼接文本则为最后显示的
                            value.append(str);
                        }
                        // 创建字体和表格元素对象
                        // 这里字体需要新建对象，否则影响其他的字体
                        XWPFParagraph xwpf = createXWPFParagraph(Align.Left);
                        xwpf.setIndentationFirstLine(400);
                        XWPFRun xwpfRun = xwpf.createRun();
                        xwpfRun.setText(value.toString());
                        xwpfRun.setBold(i == 0);
                        xwpfRun.setFontFamily(DocFont.Content.getFont());
                        xwpfRun.setFontSize(DocFont.Content.getSize());
                    } else {
                        // 没有找到匹配的field字段
                        continue;
                    }
                }
                // 每循环一个数据对象，行数+1
                writeLine();
                row++;
            }
        }

    }

    @Override
    public void writeLine() throws Exception {
        XWPFParagraph xwpf = createXWPFParagraph(Align.Center);
        XWPFRun xwpfRun = xwpf.createRun();
        xwpfRun.setText("");
        xwpfRun.setBold(true);
        xwpfRun.setFontFamily("");
        xwpfRun.setFontSize(0);
    }

    /**
     * 获取文档对象
     */
    protected XWPFDocument getDoc() {
        return doc;
    }

    /**
     * WordDoc文档XWPF对象
     */
    private XWPFDocument doc = new XWPFDocument();

    /**
     * 根据位置创建XWPF对象
     */
    private XWPFParagraph createXWPFParagraph(Align align) {
        XWPFParagraph xwpf = getDoc().createParagraph();
        xwpf.setAlignment(getAlign(align));
        return xwpf;
    }

    private ParagraphAlignment getAlign(Align align) {
        switch (align) {
        case Center:
            return ParagraphAlignment.CENTER;
        case Left:
            return ParagraphAlignment.LEFT;
        case Right:
            return ParagraphAlignment.RIGHT;
        default:
            return ParagraphAlignment.CENTER;
        }
    }

    public static void main(String[] args) throws Exception {
        StringBuilder result = new StringBuilder();
        try {
            BufferedReader br = new BufferedReader(new FileReader(new File("E:\\ssText.txt")));// 构造一个BufferedReader类来读取文件
            String s = null;
            while ((s = br.readLine()) != null) {// 使用readLine方法，一次读一行
                result.append(System.lineSeparator() + s);
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(result);

        WordDocExportor wordDocExportor = new WordDocExportor();
        byte[] bytes = ReportUtils.convertToPngByte(result.toString());
        wordDocExportor.writeImage(bytes, null, null);
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(new File("E:\\AAA.docx"));
            wordDocExportor.getDoc().write(outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
